# TODO - Allow bulk tagging of items in selection panel
# TODO - Allow selection within panel to scroll to original item in main listview
# TODO - use os.startfile to launch ebook
# TODO - Use row selection for the deletion command
# TODO - Attach a file path to a record
# TODO - Use row selection for the launch command
# TODO - Warning dialog for row/multirow deletion
# TODO - Warning dialog for multirow launch
# TODO - Better data structures for the book information / including persistance / tags
# TODO - Double click launches reader for ebook (if available)
# TODO - fix auto update of main tag list while typing in AC TextEdit
# TODO - Add item detail pane
# TODO - Add selected tray pane and bulk tag update textedit
# TODO - Allow editing of row data
# TODO - Export tags to librarything? (Contact them)
# TODO - Import data from other sources like google bookshelf, librarything etc...
# TODO - HARD - Scan book barcode using webcam, look for open-source OCR/barcode interpretation tools, find barcode spec.

import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import shelve
import os
from os.path import splitext
import isbn_dispatcher
import re
import math

class TreeItem:
    def __init__(self, data, source_list):
        """
        @type data: QVariant list
        """
        self._data = data
        self.source_list = source_list

    def childCount(self):
        return 0

    def row(self):
        return self.source_list.index(self)

    def columnCount(self):
        return len(self._data)

    def data(self, col):
        """
        @type col: int
        """
        return self._data[col]

    def parent(self):
        return None


class BookTreeModel(QAbstractItemModel):
    """
    A flat TreeModel, we can ignore parent indexes
    """
    def __init__(self, parent):
        QAbstractItemModel.__init__(self, parent)
        self.db = shelve.open('books.shelve')
        self.headers = [QVariant(txt) for txt in ['ISBN', 'Title', 'Author', 'Tags', 'File']]
#        self.db_list = self.shelve_to_list()
        self.image_dict = self.load_images()
        self.db_list = []

        for record in [self.db[key] for key in self.db.keys()]:
            data = [QVariant(QString(record[i])) for i in range(self.columnCount())]
            item = TreeItem(data, self.db_list)
            self.db_list.append(item)

#    def shelve_to_list(self):
#        """
#        Convert the shelve data into a more usable form (internal list)
#        """
#        return [[QVariant(QString(record[i])) for i in range(self.columnCount())] for record in [self.db[key] for key in self.db.keys()]]

    def __del__(self):
        self.db.close()

    def index(self, row, col, parent=QModelIndex()):
        """
        Flat Tree so col is 0 and parent invalid
        """
#        print 'index row: ', row, 'col: ', col
        if parent.isValid():
            # there are no children in a flat hierarchy
            return QModelIndex()

#        if not self.hasIndex(row, col, parent):
#            print 'ni'
#            return QModelIndex()

        try:
            item = self.db_list[row]
            return self.createIndex(row, col, item)
        except Exception, e:
            print "TreeModel - Problem creating index:"
            print e
            return QModelIndex()

    def parent(self, index):
        # Everything is top level
        return QModelIndex()

    def rowCount(self, parent=QModelIndex()):
        # Everything is top level
        return len(self.db_list)

    def columnCount(self, parent=QModelIndex()):
        return 5
        
    def data(self, index, role):
        if not index.isValid():
            return QVariant()

        if role == Qt.DecorationRole and index.column() == 0:
            res = QVariant()
            try:
                item = self.db_list[index.row()]
                res = QVariant(self.image_dict[ str(item.data(0).toString()) ])
            except Exception, e:
                res = QVariant(self.image_dict['missing'])
            finally:
                return res

        if role == Qt.EditRole:
            item = self.db_list[index.row()]
            return item.data(index.column())

        if role != Qt.DisplayRole:
            return QVariant()
        
        item = self.db_list[index.row()]
        return item.data(index.column())
#        return QVariant(QString(item[index.column()]))
        
    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable

    def setData(self, index, value, role):
        if index.isValid() and role == Qt.EditRole:
            self.db_list[index.row()]._data[index.column()] = QVariant(value.toString())
            self.emit(QtCore.SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index)
            return True
        return False

    def insertRows(self, pos, rows=1, parent=QModelIndex()):
        self.beginInsertRows(QModelIndex(), pos, pos+rows-1)
        for i in range(rows):
            data = [QVariant(QString('Unknown'))]*self.columnCount()
            self.db_list.insert(pos, TreeItem(data, self.db_list))
        self.endInsertRows()
        return True

    def removeRows(self, pos, rows=1, parent=QModelIndex()):
        self.beginRemoveRows(QModelIndex(), pos, pos+rows-1)
        for i in range(rows):
            self.db_list.pop(pos)
        self.endRemoveRows()
        return True

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.headers[section]
        return QVariant()

    def add_book(self, isbn_data):
        """
        isbn_data is list of isbn, title, author, img_src
        """
        print "Data received:"
        print isbn_data
        rc = self.rowCount()
        self.insertRows(rc)
        i0 = self.index(rc,0)
        i1 = self.index(rc,1)
        i2 = self.index(rc,2)
        self.setData(i0, QVariant(QString(isbn_data[0])), Qt.EditRole)
        self.setData(i1, QVariant(QString(isbn_data[1])), Qt.EditRole)
        self.setData(i2, QVariant(QString(isbn_data[2])), Qt.EditRole)


    def load_images(self):
        """ scan a directory for image files with the ISBN in the filename and use as icons
            return a dict of isbn to icon lookups
        """
        pixmap = QPixmap()
        img_dict = {}
        for root, dirs, files in os.walk('images'):
            for fname in files:
                root, ext = splitext(fname)
                if ext == '.jpg':
                    pixmap.load('images/' + fname)
                    icon = QIcon(pixmap)
                    img_dict[ root ] = icon
        return img_dict



class TreeModel(QAbstractItemModel):
    """
    A flat TreeModel, we can ignore parent indexes
    """
    def __init__(self, parent):
        QAbstractItemModel.__init__(self, parent)
        self.db = shelve.open('books.shelve')
        self.headers = [QVariant(txt) for txt in ['ISBN', 'Title', 'Author', 'Tags', 'File']]
        self.db_list = self.shelve_to_list()
        self.image_dict = self.load_images()

    def shelve_to_list(self):
        """
        Convert the shelve data into a more usable form (internal list)
        """
        return [[QVariant(QString(record[i])) for i in range(self.columnCount())] for record in [self.db[key] for key in self.db.keys()]]

    def __del__(self):
        self.db.close()

    def index(self, row, col, parent=QModelIndex()):
        """
        Flat Tree so col is 0 and parent invalid
        """
#        print 'index row: ', row, 'col: ', col
        if parent.isValid():
            # there are no children in a flat hierarchy
            return QModelIndex()

#        if not self.hasIndex(row, col, parent):
#            print 'ni'
#            return QModelIndex()

        try:
            item = self.db_list[row]
            return self.createIndex(row, col, item)
        except Exception, e:
            print "TreeModel - Problem creating index:"
            print e
            return QModelIndex()

    def parent(self, index):
        # Everything is top level
        return QModelIndex()

    def rowCount(self, parent=QModelIndex()):
        # Everything is top level
        return len(self.db_list)

    def columnCount(self, parent=QModelIndex()):
        return 5
        
    def data(self, index, role):
        if not index.isValid():
            return QVariant()

        if role == Qt.DecorationRole and index.column() == 0:
            res = QVariant()
            try:
                item = self.db_list[index.row()]
                res = QVariant(self.image_dict[ str(item[0].toString()) ])
            except Exception, e:
                res = QVariant(self.image_dict['missing'])
            finally:
                return res

        if role == Qt.EditRole:
            item = self.db_list[index.row()]
            return item[index.column()]

        if role != Qt.DisplayRole:
            return QVariant()
        
        item = self.db_list[index.row()]
        return item[index.column()]
#        return QVariant(QString(item[index.column()]))
        
    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable

    def setData(self, index, value, role):
        if index.isValid() and role == Qt.EditRole:
            self.db_list[index.row()][index.column()] = QVariant(value.toString())
            self.emit(QtCore.SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index)
            return True
        return False

    def insertRows(self, pos, rows=1, parent=QModelIndex()):
        self.beginInsertRows(QModelIndex(), pos, pos+rows-1)
        for i in range(rows):
            self.db_list.insert(pos, [QVariant(QString('Unknown'))]*self.columnCount())
        self.endInsertRows()
        return True

    def removeRows(self, pos, rows=1, parent=QModelIndex()):
        self.beginRemoveRows(QModelIndex(), pos, pos+rows-1)
        for i in range(rows):
            self.db_list.pop(pos)
        self.endRemoveRows()
        return True

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.headers[section]
        return QVariant()

    def add_book(self, isbn_data):
        """
        isbn_data is list of isbn, title, author, img_src
        """
        print "Data received:"
        print isbn_data
        rc = self.rowCount()
        self.insertRows(rc)
        i0 = self.index(rc,0)
        i1 = self.index(rc,1)
        i2 = self.index(rc,2)
        self.setData(i0, QVariant(QString(isbn_data[0])), Qt.EditRole)
        self.setData(i1, QVariant(QString(isbn_data[1])), Qt.EditRole)
        self.setData(i2, QVariant(QString(isbn_data[2])), Qt.EditRole)


    def load_images(self):
        """ scan a directory for image files with the ISBN in the filename and use as icons
            return a dict of isbn to icon lookups
        """
        pixmap = QPixmap()
        img_dict = {}
        for root, dirs, files in os.walk('images'):
            for fname in files:
                root, ext = splitext(fname)
                if ext == '.jpg':
                    pixmap.load('images/' + fname)
                    icon = QIcon(pixmap)
                    img_dict[ root ] = icon
        return img_dict


class SubSelectionTreeModel(QAbstractItemModel):
    """
    A flat TreeModel, which is populated only with the currently selected books. Used for the selection panel
    """
    def __init__(self, db_list, image_dict, parent):
        QAbstractItemModel.__init__(self, parent)
        self.headers = [QVariant(txt) for txt in ['ISBN', 'Title', 'Author', 'Tags', 'File']]
        self.db_list = db_list
        self.image_dict = image_dict
        self.selected_list = []
        self.filtered_model = None # needed to map indexes back to source

    def update_selection(self, selected, deselected):
        for index in selected.indexes():
            self.selected_index(index)
        for index in deselected.indexes():
            self.deselected_index(index)
        self.reset()

    def set_filtered_model(self, model):
        self.filtered_model = model

    def selected_index(self, index):
        if index.column() == 0:
            i = self.filtered_model.mapToSource(index)
#            self.selected_list.append(i.row())
            self.selected_list.append(i.internalPointer())
    def deselected_index(self, index):
        if index.column() == 0:
            i = self.filtered_model.mapToSource(index)
            self.selected_list.remove(i.internalPointer())

    def index(self, row, col, parent=QModelIndex()):
        """
        Flat Tree so col is 0 and parent invalid
        """
#        print 'index row: ', row, 'col: ', col
        if parent.isValid():
            # there are no children in a flat hierarchy
            return QModelIndex()

#        if not self.hasIndex(row, col, parent):
#            print 'ni'
#            return QModelIndex()

        try:
            item = self.db_list.index(self.selected_list[row])
            return self.createIndex(row, col, item)
        except Exception, e:
            print "SubSelectionTreeModel - Problem creating index:"
            print e
            return QModelIndex()

    def parent(self, index):
        # Everything is top level
        return QModelIndex()

    def rowCount(self, parent=QModelIndex()):
        # Everything is top level
        return len(self.selected_list)

    def columnCount(self, parent=QModelIndex()):
        return 5
        
    def data(self, index, role):
        if not index.isValid():
            return QVariant()

        if role != Qt.DecorationRole:
            return QVariant()

        if role == Qt.DecorationRole and index.column() == 0:
            res = QVariant()
            try:
                item = self.selected_list[index.row()]
                res = QVariant(self.image_dict[ str(item.data(0).toString()) ])
            except Exception, e:
                res = QVariant(self.image_dict['missing'])
            finally:
                return res

#        if role == Qt.EditRole:
#            item = self.db_list[index.row()]
#            return item[index.column()]

#        if role != Qt.DisplayRole:
#            return QVariant()
        
#        item = self.db_list[self.selected_list[index.row()]]
#        return item[index.column()]
#        return QVariant(QString(item[index.column()]))
        
    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable 

#    def setData(self, index, value, role):
#        if index.isValid() and role == Qt.EditRole:
#            self.db_list[index.row()][index.column()] = QVariant(value.toString())
#            self.emit(QtCore.SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index)
#            return True
#        return False

#    def insertRows(self, pos, rows=1, parent=QModelIndex()):
#        self.beginInsertRows(QModelIndex(), pos, pos+rows-1)
#        for i in range(rows):
#            self.db_list.insert(pos, [QVariant(QString('Unknown'))]*self.columnCount())
#        self.endInsertRows()
#        return True

#    def removeRows(self, pos, rows=1, parent=QModelIndex()):
#        self.beginRemoveRows(QModelIndex(), pos, pos+rows-1)
#        for i in range(rows):
#            self.db_list.pop(pos)
#        self.endRemoveRows()
#        return True

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.headers[section]
        return QVariant()

            
class CompletionSet(list):
    def __init__(self):
        list.__init__(self)

    def clear(self):
        while(len(self)):
            self.pop()

class AllTagsTextEdit(QTextEdit):
    def __init__(self, parent=None, tags=CompletionSet()):
        QTextEdit.__init__(self, parent)
        self.tags = tags
        self.connect(self,QtCore.SIGNAL('textChanged()'),self.parse_tags)

    def parse_tags(self):
        """
        tokenise contents of QTextEdit and place in tags list
        """
        string = self.toPlainText()
        self.tags.clear()
        for word in string.split(' '):
            self.tags.append(word)
        self.tags.sort()
        
    def update_display(self):
        self.blockSignals(True)
        self.clear()
        for tag in self.tags:
            self.insertPlainText(tag)
            self.insertPlainText(' ')
        self.blockSignals(False)

class AutoCompleteTextEdit(QTextEdit):
    def __init__(self, parent=None):
        QTextEdit.__init__(self, parent)
        self.completionSet = []
        self.connect(self,QtCore.SIGNAL('textChanged()'),self.suggest_completion)
        self.connect(self,QtCore.SIGNAL('text_finalised'),self.update_completionSet)

    def update_completionSet(self):
        # Add tags defined here to full set if needed

        # tokenise contents of QTextEdit 
        string = self.toPlainText()
        tags = []
        for word in string.split(' '):
            tags.append(word)

        for tag in tags:
            if not tag in self.completionSet:
                self.completionSet.append(tag)
        self.emit(QtCore.SIGNAL('completion_set_updated'))

    def set_completion_set(self, set):
        self.completionSet = set

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Backspace:
            # don't do autocompletion after backspace
            self.blockSignals( True )
            QTextEdit.keyPressEvent(self, e)
            self.blockSignals( False )
        elif e.key() == Qt.Key_Delete:
            # don't do autocompletion after backspace
            self.blockSignals( True )
            QTextEdit.keyPressEvent(self, e)
            self.blockSignals( False )
        elif e.key() == Qt.Key_Space:
            # accept completion
            if self.textCursor().hasSelection():
                self.move_cursor_to_end_of_word()
            QTextEdit.keyPressEvent(self, e)
        elif e.key() == Qt.Key_Return:
            self.emit(QtCore.SIGNAL('text_finalised'))
        else:
            QTextEdit.keyPressEvent(self, e)

    def move_cursor_to_end_of_word(self):
        tc = self.textCursor()
        curPos = tc.position()
        tc.movePosition(QTextCursor.EndOfWord, QTextCursor.MoveAnchor)
        self.setTextCursor(tc)

    def suggest_completion(self):
        """  Insert a text completion suggestion at the end of the current word """
        self.blockSignals( True )
        
        # first check we are at the end of a word
        if not self.is_end_of_word():
            print "Not end of word"
            self.blockSignals( False )
            return

        stem = self.current_stem()
        completions = self.get_completions(stem)
        print stem
        print completions
        if len(completions):
            suggested_end = self.get_suggested_end(stem, completions[0])
            self.insert_suggested_end(suggested_end)
        self.blockSignals( False )


    def get_suggested_end(self, stem, suggestion):
        return suggestion[len(stem):]

    def is_end_of_word(self):
        """
        Returns true if there is whitespace or end of document after the cursor
        position (i.e. we are at the end of a word)
        """
        if self.textCursor().atBlockEnd():
            return True
        curPos = self.textCursor().position()
        nextChar = self.textCursor().block().text()[curPos]
        if nextChar == ' ':
            return True
        return False

    def current_stem(self):
        tc = self.textCursor()
        curPos = tc.position()
        tc.movePosition(QTextCursor.StartOfWord, QTextCursor.KeepAnchor)
        stem = tc.selectedText()
        tc.setPosition(curPos, QTextCursor.KeepAnchor)
        self.setTextCursor(tc)
        return stem

    def insert_suggested_end(self, suggestion):
        """
        Add the suggested end as some selected text at the end of the word
        """
        tc = self.textCursor()
        curPos = tc.position()
        tc.insertText(suggestion)
        tc.setPosition(curPos, QTextCursor.KeepAnchor)
        self.setTextCursor(tc)
        
    def get_completions(self, stem):
        """ Return list of potential completions. Empty if none """
        res = []
        if len(stem) == 0:
            return res
        for word in self.completionSet:
            if len(word) > len(stem):
                if str(word[:len(stem)]) == str(stem):
                    res.append(word)
        return res

class SearchWidget(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        layout = QHBoxLayout()
        self.lineedit = QLineEdit()
        self.button = QPushButton()

        layout.addWidget(self.lineedit)
        layout.addWidget(self.button)
        self.setLayout(layout)
        self.connect(self.lineedit,QtCore.SIGNAL('textChanged(QString)'), self.textChanged)
#        self.connect(self.button,QtCore.SIGNAL('pressed()'), self.textChanged)
        
    def textChanged(self, text):
        self.emit(QtCore.SIGNAL('textChanged(QString)'), text)

    
class BookTreeWidget(QTreeWidget):
    def __init__(self, parent=None):
        QTreeWidget.__init__(self, parent)

        self.setColumnCount(4)
        self.setHeaderLabels( ['Title', 'Author', 'ISBN', 'Tags'] )

        self.image_dict = self.load_images()
        self.load_data()

    def load_data(self):
        """ 
        Load entries from shelve db file
        """
        db = shelve.open('books.shelve')
        for key in db:
            isbn, title, author = db[key]
            try:
                icon = self.image_dict[isbn]
            except Exception, e:
                icon = self.image_dict['missing']
            item = QTreeWidgetItem(self)
            item.setText(0, title)
            item.setText(1, author)
            item.setText(2, isbn)
            item.setIcon(0, icon )

        size = QSize()
        size.scale(150, 150, Qt.KeepAspectRatio)
        self.setIconSize(size)

    def load_images(self):
        """ scan a directory for image files with the ISBN in the filename and use as icons
            return a dict of isbn to icon lookups
        """
        pixmap = QPixmap()

        img_dict = {}

        for root, dirs, files in os.walk('images'):
            for fname in files:
                root, ext = splitext(fname)
                if ext == '.jpg':
                    pixmap.load('images/' + fname)
                    icon = QIcon(pixmap)
                    img_dict[ root ] = icon
        return img_dict


class MySortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self, parent):
        QSortFilterProxyModel.__init__(self, parent)

    def filterAcceptsRow(self, row, parent):
        i0 = self.sourceModel().index(row, 0, parent)
        i1 = self.sourceModel().index(row, 1, parent)
        i2 = self.sourceModel().index(row, 2, parent)

        return self.sourceModel().data(i0, 0).toString().contains(self.filterRegExp()) \
               or self.sourceModel().data(i1, 0).toString().contains(self.filterRegExp()) \
               or self.sourceModel().data(i2, 0).toString().contains(self.filterRegExp())

    def filter_on_word(self, text):
        self.invalidateFilter()
        self.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive, QRegExp.FixedString))

    def removeRows(self, pos, rows=1, parent=QModelIndex()):
        self.beginRemoveRows(QModelIndex(), pos, pos+rows-1)
        for i in range(rows):
            sourceIndex = self.mapToSource(self.index(pos, 0))
            self.sourceModel().removeRows(sourceIndex.row())
        self.endRemoveRows()
        return True

class ImportWindow(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setupWidget()

    def setupWidget(self):
        self.layout = QHBoxLayout(self)
        self.import_te = QTextEdit(self)
        self.result_te = QTextEdit(self)
        self.submit_button = QPushButton('Submit', self)
        self.dispatcher = isbn_dispatcher.ISBNDispatcher(self)
        self.dispatcher.start()

        self.layout.addWidget(self.import_te)
        self.layout.addWidget(self.result_te)
        self.layout.addWidget(self.submit_button)
        self.setLayout(self.layout)

        self.connect(self.submit_button,QtCore.SIGNAL('pressed()'), self.parse_input)
        self.connect(self.dispatcher,QtCore.SIGNAL('isbn_data'), self.isbn_result)

    def isbn_result(self, res):
        self.result_te.insertPlainText(str(res))
        self.emit(QtCore.SIGNAL('isbn_data'), res)

    def parse_input(self):
        """
        Find ISBNs in the input text and submit to the dispatcher
        """
        string = self.import_te.toPlainText()
        string = string.simplified()
        isbns = []
        for word in string.split(" "):
            word = str(word)
            word = word.strip()
            isbn = re.sub(r'\D', '', word)
            self.dispatcher.isbn_submit(isbn)
        self.import_te.clear()

class BookTreeView(QTreeView):
    def __init__(self, parent=None):
        QTreeView.__init__(self, parent)
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setWordWrap(True)

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Delete:
            # find current selection and delete it
            # warn dialog require
#            items_to_delete = []
            row_indexes = []
            for index in self.selectionModel().selectedIndexes():
                if index.column() == 0: # only do once per row
                    row_indexes.append(index.row())
            row_indexes.sort(reverse=True) # remove latest first
            for i in row_indexes:
                self.model().removeRows( i, 1 )
        elif e.key() == Qt.Key_Return:
            # find current selection and launch attached file if any
            # multiple launch warning dialog required
            pass
        else:
            QTreeView.keyPressEvent(self, e)
    
class SelectedIconView(QListView):
    def __init__(self, parent=None):
        QListView.__init__(self, parent)
        self.setResizeMode(QListView.Adjust)
        self.setViewMode(QListView.IconMode)

    def reportSize(self):
        print self.size().width()
        print self.size().height()

    def resizeEvent(self, e):
        self.resize_icons_to_fit()
#        QListView.resizeEvent(self, e)

    def resize_icons_to_fit(self):
        icon_count = self.model().rowCount()
        if not icon_count:
            return
        width = self.size().width()
        height = self.size().height()
        window_area = width*height
        icon_area = self.iconSize().width() * self.iconSize().height()

#        covered_area = icon_count * icon_area # we want this close to 1
        area_per_icon = window_area / float(icon_count)
        w = int(math.sqrt(area_per_icon))
        self.setIconSize( QSize(w, w))
    
#        ratio = window_area / (icon_count * float(icon_area))
#        if ratio < 1.:
#             icons aren't being shown, so resize
#            w = self.iconSize().width() /2.
#            self.setIconSize( QSize(w, w))
#        elif ratio > 3.:
#             icons are small in relation to available space, enlarge them
#            w = self.iconSize().width() *2.
#            self.setIconSize( QSize(w, w))
            

            

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        self.setupWidget()

        self.setWindowTitle(self.tr("Auto complete widget test"))
        self.resize(800, 50)

    def setupWidget(self):
        window = QWidget()
        layout = QVBoxLayout()
        window.setLayout(layout)
        self.setCentralWidget(window)

        tags_te = AllTagsTextEdit()
        ac_textedit = AutoCompleteTextEdit()
        ac_textedit.set_completion_set(tags_te.tags)

        self.connect(ac_textedit,QtCore.SIGNAL('completion_set_updated'), tags_te.update_display)

#        layout.addWidget(ac_textedit)
#        layout.addWidget(tags_te)

#        tree_widget = BookTreeWidget(self)
#        layout.addWidget(tree_widget)

#        m = TreeModel(self)
        m = BookTreeModel(self)
#        tv = QTreeView(self)
#        tv.setModel(m)
#        tv.setItemsExpandable(False)
#        tv.setRootIsDecorated(False)
#        tv.setSortingEnabled(True)
#        tv.setAlternatingRowColors(True)

#        proxyModel = QSortFilterProxyModel(self)
        proxyModel = MySortFilterProxyModel(self)
        proxyModel.setSourceModel(m)


#        proxyModel.setFilterRegExp(QRegExp("C++", Qt.CaseInsensitive, QRegExp.FixedString))
#        proxyModel.setFilterKeyColumn(2)

        fv = BookTreeView(self)
        fv.setModel(proxyModel)
#        fv.setModel(m)
        fv.setSortingEnabled(True)
        fv.sortByColumn(0, Qt.AscendingOrder)

        fv.setItemsExpandable(False)
        fv.setRootIsDecorated(False)
        fv.setSortingEnabled(True)
        fv.setAlternatingRowColors(True)

        size = QSize()
        size.scale(150, 150, Qt.KeepAspectRatio)
        fv.setIconSize(size)
        fv.resizeColumnToContents(0)
        fv.setColumnWidth(1, 400)
        fv.resizeColumnToContents(2)
            
        
        searchbox = SearchWidget(self)

#        centre_widget = QWidget()
#        centre_layout = QHBoxLayout()
#        centre_layout.addWidget(fv)
#        centre_layout.addWidget(QTextEdit())
#        centre_widget.setLayout(centre_layout)
        centre_widget = QSplitter(self)
        centre_widget.addWidget(fv)

        
        lv = SelectedIconView(self)
        sub_model = SubSelectionTreeModel(m.db_list, m.image_dict, self)
        sub_model.set_filtered_model(proxyModel)
        lv.setModel(sub_model)

        lv.setIconSize(size)

#        centre_widget.addWidget(lv) # placeholder for detail view
        centre_widget.addWidget(QTextEdit()) # placeholder for detail view

        centre_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        tabs = QTabWidget(self)
        tabs.addTab(centre_widget, 'Book List')
        self.import_window = ImportWindow(self)
        tabs.addTab(self.import_window, 'Import Books')

        layout.addWidget(searchbox)
#        layout.addWidget(centre_widget)
        layout.addWidget(tabs)
        layout.addWidget(lv)

        self.connect(searchbox, QtCore.SIGNAL('textChanged(QString)'), proxyModel.filter_on_word) 
        self.connect(self.import_window,QtCore.SIGNAL('isbn_data'), m.add_book)

        self.connect(fv.selectionModel(),QtCore.SIGNAL('selectionChanged(QItemSelection, QItemSelection)'), sub_model.update_selection)
        self.connect(fv.selectionModel(),QtCore.SIGNAL('selectionChanged(QItemSelection, QItemSelection)'), lv.resize_icons_to_fit)
        
        


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

